/*
Copyright [2020] [https://www.xiaonuo.vip]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Snowy采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：

1.请不要删除和修改根目录下的LICENSE文件。
2.请不要删除和修改Snowy源码头部的版权声明。
3.请保留源码和相关描述文件的项目出处，作者声明等。
4.分发源码时候，请注明软件出处 https://gitee.com/xiaonuobase/snowy
5.在修改包名，模块名称，项目代码等时，请注明软件出处 https://gitee.com/xiaonuobase/snowy
6.若您的项目无法满足以上几点，可申请商业授权，获取Snowy商业授权许可，请在官网购买授权，地址为 https://www.xiaonuo.vip
 */
package vip.xiaonuo.app.modular.pay.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import vip.xiaonuo.app.core.consts.AppConstant;
import vip.xiaonuo.app.modular.pay.result.PayInfoResult;
import vip.xiaonuo.app.modular.recharge.entity.AppRechargeOrder;
import vip.xiaonuo.app.modular.recharge.service.AppRechargeOrderService;
import vip.xiaonuo.app.modular.vip.entity.AppVipOrder;
import vip.xiaonuo.app.modular.vip.service.AppVipOrderService;
import vip.xiaonuo.core.exception.ServiceException;
import vip.xiaonuo.core.exception.enums.ParamException;
import vip.xiaonuo.core.exception.enums.ParamExceptionEnum;
import vip.xiaonuo.core.factory.PageFactory;
import vip.xiaonuo.core.pojo.page.PageResult;
import vip.xiaonuo.core.util.HttpServletUtil;
import vip.xiaonuo.core.util.IpAddressUtil;
import vip.xiaonuo.core.util.PoiUtil;
import vip.xiaonuo.app.modular.pay.entity.AppWxpayConfig;
import vip.xiaonuo.app.modular.pay.enums.AppWxpayConfigExceptionEnum;
import vip.xiaonuo.app.modular.pay.mapper.AppWxpayConfigMapper;
import vip.xiaonuo.app.modular.pay.param.mgr.AppWxpayConfigParam;
import vip.xiaonuo.app.modular.pay.service.AppWxpayConfigService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.PrivateKey;
import java.util.List;

/**
 * 微信支付service接口实现类
 *
 * @author 郑雄伟
 * @date 2023-10-08 17:01:13
 */
@Service
public class AppWxpayConfigServiceImpl extends ServiceImpl<AppWxpayConfigMapper, AppWxpayConfig> implements AppWxpayConfigService {

    @Resource
    private AppRechargeOrderService appRechargeOrderService;
    @Resource
    private AppVipOrderService appVipOrderService;

    /**
     * 如果你是使用Apache HttpClient的商户开发者，可以使用它构造HttpClient。得到的HttpClient在执行请求时将自动携带身份认证信息，并检查应答的微信支付签名。
     */
    private static CloseableHttpClient httpClient = null;

    /**
     * 初始化
     */
    public void setup(AppWxpayConfig appWxpayConfig) {

        // 加载商户私钥（privateKey：私钥字符串）
        PrivateKey merchantPrivateKey = null;
        try {
            merchantPrivateKey = PemUtil
                    .loadPrivateKey(new ByteArrayInputStream(appWxpayConfig.getPrivateKey().getBytes("utf-8")));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        // 加载平台证书（mchId：商户号,mchSerialNo：商户证书序列号,apiV3Key：V3密钥）
        AutoUpdateCertificatesVerifier verifier = null;
        try {
            verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials(appWxpayConfig.getMchId(), new PrivateKeySigner(appWxpayConfig.getMchSerialNo(), merchantPrivateKey)),
                    appWxpayConfig.getAppKey().getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        // 初始化httpClient
        httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(appWxpayConfig.getMchId(), appWxpayConfig.getMchSerialNo(), merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier)).build();
    }

    @Override
    public AppWxpayConfig getByAppId(String appIdStr) {
        LambdaQueryWrapper<AppWxpayConfig> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AppWxpayConfig::getAppId,appIdStr);
        return this.getOne(queryWrapper);
    }

    @Override
    public AppWxpayConfig getWeightPayInfo() {
        //取第一条
        AppWxpayConfig appWxpayConfig = this.list().get(0);
        return appWxpayConfig;
    }

    @Override
    public PayInfoResult transactions(Long userId, String mobileSpec, Integer projectType, Integer packageId, BigDecimal money) {

        //获取HttpServletRequest
        HttpServletRequest request = HttpServletUtil.getRequest();
        //获取ip地址
        String ip = IpAddressUtil.getIp(request);

        //从数据库支付请求必要参数
        AppWxpayConfig info = getWeightPayInfo();
        //初始化商户连接
        setup(info);
        //判断payType
        switch (projectType) {
            case 1: // 支付类型1 (钻石充值)
                //添加订单
                AppRechargeOrder appRechargeOrder
                        = appRechargeOrderService.tradeWapPay(
                        userId,
                        mobileSpec,
                        packageId,
                        money,
                        AppConstant.WX_PAY_TWO);
                //支付预处理
                //金额
                int moneyTotal= appRechargeOrder.getMoney().multiply(new BigDecimal(100)).intValue();//单位分
                //回调地址拼接
                String notify_url = info.getNotifyUrl() + "/" + info.getAppId() + "/1";
                //请求URL
                HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/h5");
                // 请求body参数
                String reqdata = "{"
                        + "\"amount\": {"
                        + "\"total\": " + moneyTotal + ","
                        + "\"currency\": \"CNY\""
                        + "},"
                        + "\"scene_info\": {"
                        + "\"payer_client_ip\":\"" + ip + "\","
                        + "\"h5_info\": {"
                        + "\"type\": \"Android\"" + "}},"
                        + "\"mchid\": \"" + info.getMchId() + "\","
                        + "\"description\":\"钻石充值\","
                        + "\"notify_url\": \"" + notify_url + "\","
                        + "\"out_trade_no\": \"" + appRechargeOrder.getOrderNo() + "\","
                        + "\"appid\": \"" + info.getAppId() + "\"" + "}";

                StringEntity entity = new StringEntity(reqdata, "utf-8");
                entity.setContentType("application/json");
                httpPost.setEntity(entity);
                httpPost.setHeader("Accept", "application/json");

                PayInfoResult payInfoResult = new PayInfoResult();
                String signMap = "";
                try {
                    //完成签名并执行请求
                    CloseableHttpResponse response = httpClient.execute(httpPost);
                    try {
                        int statusCode = response.getStatusLine().getStatusCode();
                        if (statusCode == 200) {
                            signMap = EntityUtils.toString(response.getEntity());
                        } else if (statusCode == 204) {
                            System.out.println("success");
                        } else {
                            System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
                            throw new IOException("request failed");
                        }

                    } finally {
                        response.close();
                    }
                    JSONObject jsonObject = JSON.parseObject(signMap);
                    String h5_url = jsonObject.get("h5_url").toString();
                    payInfoResult.setOrderNo(appRechargeOrder.getOrderNo());
                    payInfoResult.setPayUrl(h5_url);
                    return payInfoResult;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            case 2:

                //添加订单
                AppVipOrder appVipOrder = appVipOrderService.tradeWapPay(
                        userId,
                        mobileSpec,
                        packageId,
                        money,
                        AppConstant.WX_PAY_TWO);
                //支付预处理
                //金额
                int moneyTotal2= appVipOrder.getMoney().multiply(new BigDecimal(100)).intValue();//单位分
                //回调地址拼接
                String notify_url2 = info.getNotifyUrl() + "/" + info.getAppId() + "/1";
                //请求URL
                HttpPost httpPost2 = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/h5");
                // 请求body参数
                String reqdata1 = "{"
                        + "\"amount\": {"
                        + "\"total\": " + moneyTotal2 + ","
                        + "\"currency\": \"CNY\""
                        + "},"
                        + "\"scene_info\": {"
                        + "\"payer_client_ip\":\"" + ip + "\","
                        + "\"h5_info\": {"
                        + "\"type\": \"Android\"" + "}},"
                        + "\"mchid\": \"" + info.getMchId() + "\","
                        + "\"description\":\"会员充值\","
                        + "\"notify_url\": \"" + notify_url2 + "\","
                        + "\"out_trade_no\": \"" + appVipOrder.getOrderNo() + "\","
                        + "\"appid\": \"" + info.getAppId() + "\"" + "}";

                StringEntity entity2 = new StringEntity(reqdata1, "utf-8");
                entity2.setContentType("application/json");
                httpPost2.setEntity(entity2);
                httpPost2.setHeader("Accept", "application/json");

                PayInfoResult payInfoResult2 = new PayInfoResult();
                String signMap2 = "";
                try {
                    //完成签名并执行请求
                    CloseableHttpResponse response = httpClient.execute(httpPost2);
                    try {
                        int statusCode = response.getStatusLine().getStatusCode();
                        if (statusCode == 200) {
                            signMap2 = EntityUtils.toString(response.getEntity());
                        } else if (statusCode == 204) {
                            System.out.println("success");
                        } else {
                            System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
                            throw new IOException("request failed");
                        }

                    } finally {
                        response.close();
                    }
                    JSONObject jsonObject2 = JSON.parseObject(signMap2);
                    String h5_url2 = jsonObject2.get("h5_url").toString();
                    payInfoResult2.setOrderNo(appVipOrder.getOrderNo());
                    payInfoResult2.setPayUrl(h5_url2);
                    return payInfoResult2;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            default:
                throw new ParamException(ParamExceptionEnum.PARAM_ERROR);
        }
    }

    @Override
    public PageResult<AppWxpayConfig> page(AppWxpayConfigParam appWxpayConfigParam) {
        QueryWrapper<AppWxpayConfig> queryWrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotNull(appWxpayConfigParam)) {

            // 根据权重1-100 查询
            if (ObjectUtil.isNotEmpty(appWxpayConfigParam.getWeight())) {
                queryWrapper.lambda().eq(AppWxpayConfig::getWeight, appWxpayConfigParam.getWeight());
            }
            // 根据商户名称 查询
            if (ObjectUtil.isNotEmpty(appWxpayConfigParam.getMchName())) {
                queryWrapper.lambda().eq(AppWxpayConfig::getMchName, appWxpayConfigParam.getMchName());
            }
            // 根据AppId 查询
            if (ObjectUtil.isNotEmpty(appWxpayConfigParam.getAppId())) {
                queryWrapper.lambda().eq(AppWxpayConfig::getAppId, appWxpayConfigParam.getAppId());
            }
            // 根据直连商户id 查询
            if (ObjectUtil.isNotEmpty(appWxpayConfigParam.getMchId())) {
                queryWrapper.lambda().eq(AppWxpayConfig::getMchId, appWxpayConfigParam.getMchId());
            }
            // 根据商户api证书序列号 查询
            if (ObjectUtil.isNotEmpty(appWxpayConfigParam.getMchSerialNo())) {
                queryWrapper.lambda().eq(AppWxpayConfig::getMchSerialNo, appWxpayConfigParam.getMchSerialNo());
            }
            // 根据V3密钥 查询
            if (ObjectUtil.isNotEmpty(appWxpayConfigParam.getAppKey())) {
                queryWrapper.lambda().eq(AppWxpayConfig::getAppKey, appWxpayConfigParam.getAppKey());
            }
            // 根据开发者私钥 查询
            if (ObjectUtil.isNotEmpty(appWxpayConfigParam.getPrivateKey())) {
                queryWrapper.lambda().eq(AppWxpayConfig::getPrivateKey, appWxpayConfigParam.getPrivateKey());
            }
            // 根据异步回调地址（多种订单模式根据 xxx_截取判断） 查询
            if (ObjectUtil.isNotEmpty(appWxpayConfigParam.getNotifyUrl())) {
                queryWrapper.lambda().eq(AppWxpayConfig::getNotifyUrl, appWxpayConfigParam.getNotifyUrl());
            }
            // 根据支付成功返回地址 查询
            if (ObjectUtil.isNotEmpty(appWxpayConfigParam.getReturnUrl())) {
                queryWrapper.lambda().eq(AppWxpayConfig::getReturnUrl, appWxpayConfigParam.getReturnUrl());
            }
        }
        return new PageResult<>(this.page(PageFactory.defaultPage(), queryWrapper));
    }

    @Override
    public List<AppWxpayConfig> list(AppWxpayConfigParam appWxpayConfigParam) {
        return this.list();
    }

    @Override
    public void add(AppWxpayConfigParam appWxpayConfigParam) {
        AppWxpayConfig appWxpayConfig = new AppWxpayConfig();
        BeanUtil.copyProperties(appWxpayConfigParam, appWxpayConfig);
        this.save(appWxpayConfig);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<AppWxpayConfigParam> appWxpayConfigParamList) {
        appWxpayConfigParamList.forEach(appWxpayConfigParam -> {
            this.removeById(appWxpayConfigParam.getId());
        });
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(AppWxpayConfigParam appWxpayConfigParam) {
        AppWxpayConfig appWxpayConfig = this.queryAppWxpayConfig(appWxpayConfigParam);
        BeanUtil.copyProperties(appWxpayConfigParam, appWxpayConfig);
        this.updateById(appWxpayConfig);
    }

    @Override
    public AppWxpayConfig detail(AppWxpayConfigParam appWxpayConfigParam) {
        return this.queryAppWxpayConfig(appWxpayConfigParam);
    }

    /**
     * 获取微信支付
     *
     * @author 郑雄伟
     * @date 2023-10-08 17:01:13
     */
    private AppWxpayConfig queryAppWxpayConfig(AppWxpayConfigParam appWxpayConfigParam) {
        AppWxpayConfig appWxpayConfig = this.getById(appWxpayConfigParam.getId());
        if (ObjectUtil.isNull(appWxpayConfig)) {
            throw new ServiceException(AppWxpayConfigExceptionEnum.NOT_EXIST);
        }
        return appWxpayConfig;
    }

    @Override
    public void export(AppWxpayConfigParam appWxpayConfigParam) {
        List<AppWxpayConfig> list = this.list(appWxpayConfigParam);
        PoiUtil.exportExcelWithStream("SnowyAppWxpayConfig.xls", AppWxpayConfig.class, list);
    }

}
